package com.neural;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;


/**
 * Java Neural Network Example
 * Handwriting Recognition
 * by Jeff Heaton (http://www.jeffheaton.com) 1-2002
 * -------------------------------------------------
 * This class is used to provide a small component that
 * the user can draw handwritten letters into. This class
 * also contains the routines necessary to crop and downsample
 * the written character.
 *
 * @author Jeff Heaton (http://www.jeffheaton.com)
 * @version 1.0
 */
public class Entry {

    /**
     * The downsample width for the application.
     */
    public static final int DOWNSAMPLE_WIDTH = 10;

    /**
     * The down sample height for the application.
     */
    public static final int DOWNSAMPLE_HEIGHT = 10;


    /**
     * Specifies the left boundary of the cropping
     * rectangle.
     */
    protected int downSampleLeft;

    /**
     * Specifies the right boundary of the cropping
     * rectangle.
     */
    protected int downSampleRight;

    /**
     * Specifies the top boundary of the cropping
     * rectangle.
     */
    protected int downSampleTop;

    /**
     * Specifies the bottom boundary of the cropping
     * rectangle.
     */
    protected int downSampleBottom;

    /**
     * The downsample ratio for x.
     */
    protected double ratioX;

    /**
     * The downsample ratio for y
     */
    protected double ratioY;

    /**
     * The pixel map of what the user has drawn.
     * Used to downsample it.
     */
    protected int pixelMap[];

    /**
     * The constructor.
     */
    public Entry() {
    }


    /**
     * This method is called internally to
     * see if there are any pixels in the given
     * scan line. This method is used to perform
     * autocropping.
     *
     * @param y The horizontal line to scan.
     * @return True if there were any pixels in this
     * horizontal line.
     */
    protected boolean hLineClear(Image entryImage, int y) {
        int w = entryImage.getWidth(null);
        for (int i = 0; i < w; i++) {
            if (pixelMap[ (y * w) + i] != -1) {
                return false;
            }
        }
        return true;
    }

    /**
     * This method is called to determine ....
     *
     * @param x The vertical line to scan.
     * @return True if there are any pixels in the
     * specified vertical line.
     */
    protected boolean vLineClear(Image entryImage, int x) {
        int w = entryImage.getWidth(null);
        int h = entryImage.getHeight(null);
        for (int i = 0; i < h; i++) {
            if (pixelMap[ (i * w) + x] != -1) {
                return false;
            }
        }
        return true;
    }

    /**
     * This method is called to automatically
     * crop the image so that whitespace is
     * removed.
     *
     * @param w The width of the image.
     * @param h The height of the image
     */
    protected void findBounds(Image entryImage, int w, int h) {
        // top line
        for (int y = 0; y < h; y++) {
            if (!hLineClear(entryImage, y)) {
                downSampleTop = y;
                break;
            }

        }
        // bottom line
        for (int y = h - 1; y >= 0; y--) {
            if (!hLineClear(entryImage, y)) {
                downSampleBottom = y;
                break;
            }
        }
        // left line
        for (int x = 0; x < w; x++) {
            if (!vLineClear(entryImage, x)) {
                downSampleLeft = x;
                break;
            }
        }

        // right line
        for (int x = w - 1; x >= 0; x--) {
            if (!vLineClear(entryImage, x)) {
                downSampleRight = x;
                break;
            }
        }
    }

    /**
     * Called to downsample a quadrant of the image.
     *
     * @param x The x coordinate of the resulting
     * downsample.
     * @param y The y coordinate of the resulting
     * downsample.
     * @return Returns true if there were ANY pixels
     * in the specified quadrant.
     */
    protected boolean downSampleQuadrant(Image entryImage, int x, int y) {
        int w = entryImage.getWidth(null);
        int startX = (int) (downSampleLeft + (x * ratioX));
        int startY = (int) (downSampleTop + (y * ratioY));
        int endX = (int) (startX + ratioX);
        int endY = (int) (startY + ratioY);

        for (int yy = startY; yy <= endY; yy++) {
            for (int xx = startX; xx <= endX; xx++) {
                int loc = xx + (yy * w);

                if (pixelMap[loc] != -1) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Called to downsample the image and store
     * it in the down sample component.
     */
    public SampleData downSample(String letter, Image entryImage) {
        int w = entryImage.getWidth(null);
        int h = entryImage.getHeight(null);

        SampleData data = null;

        PixelGrabber grabber = new PixelGrabber(
            entryImage,
            0,
            0,
            w,
            h,
            true);
        try {

            grabber.grabPixels();
            pixelMap = (int[]) grabber.getPixels();
            findBounds(entryImage, w, h);

            // now downsample
            data = new SampleData(letter, DOWNSAMPLE_WIDTH, DOWNSAMPLE_HEIGHT);

            ratioX = (double) (downSampleRight -
                               downSampleLeft) / (double) data.getWidth();
            ratioY = (double) (downSampleBottom -
                               downSampleTop) / (double) data.getHeight();

            for (int y = 0; y < data.getHeight(); y++) {
                for (int x = 0; x < data.getWidth(); x++) {
                    if (downSampleQuadrant(entryImage, x, y)) {
                        data.setData(x, y, true);
                    }
                    else {
                        data.setData(x, y, false);
                    }
                }
            }

            //sample.repaint();
            //repaint();
        }
        catch (InterruptedException e) {
        }

        return data;
    }

}
